Clicky

Util

Cellular Automata

Conway’s Game Of Life

Implementation of Conway’s Game of Life in python.

def new_generation(cells: list) -> list:
    next_generation = []
    for i in range(len(cells)):
        next_generation_row = []
        for j in range(len(cells[i])):
            neighbor_count = 0
            for n in [-1, 0, 1]:
                for m in [-1, 0, 1]:
                    if (not ((len(cells[i])-1)>=j+m>=0<=i+n<=(len(cells)-1)) or 
                        (n==0 and m==0)):
                        continue
                    neighbor_count += cells[i+n][j+m]

            next_generation_row.append(
                int((cells[i][j] and 2 <= neighbor_count <= 3) or 
                    (not cells[i][j] and neighbor_count == 3)))
        next_generation.append(next_generation_row)
    return next_generation

Creating a UI with Pygame:

import pygame as pg
import sys
import numpy as np

def new_generation(cells: list) -> list:
    next_generation = []
    for i in range(len(cells)):
        next_generation_row = []
        for j in range(len(cells[i])):
            neighbor_count = 0
            for n in [-1, 0, 1]:
                for m in [-1, 0, 1]:
                    if (not ((len(cells[i])-1)>=j+m>=0<=i+n<=(len(cells)-1)) or 
                        (n==0 and m==0)):
                        continue
                    neighbor_count += cells[i+n][j+m]

            next_generation_row.append(
                int((cells[i][j] and 2 <= neighbor_count <= 3) or 
                    (not cells[i][j] and neighbor_count == 3)))
        next_generation.append(next_generation_row)
    return next_generation

def write_text(string, x, y, font_size, screen):
    text = pg.font.Font('freesansbold.ttf', font_size).render(string, True, (0, 0, 0))
    text_rect = text.get_rect()
    text_rect.topleft = (x, y)
    screen.blit(text, text_rect)

pg.init()

screen_size = (800, 800)
simulation_size = (100, 100)
simulation_frame_rate = 30
paused_frame_rate = 120
frame_cap = 100
screen = pg.display.set_mode(screen_size)

clock = pg.time.Clock()

WHITE = [255, 255, 255]
BLACK = [0, 0, 0]

colors = np.array([WHITE, BLACK])
grid = np.zeros(shape=simulation_size, dtype=int)
paused = True
drag = False

while True:
    clock.tick(simulation_frame_rate if not paused else paused_frame_rate)
    
    for event in pg.event.get():
        if event.type == pg.QUIT:
            pg.quit()
            sys.exit()
        if event.type == pg.KEYDOWN:
            keys = pg.key.get_pressed()
            if keys[pg.K_SPACE]:
                paused = not paused
            if keys[pg.K_w]:
                simulation_frame_rate += 5 if not keys[pg.K_LCTRL] else 1
                if simulation_frame_rate >= frame_cap:
                    simulation_frame_rate = frame_cap
            if keys[pg.K_s]:
                simulation_frame_rate -= 5 if not keys[pg.K_LCTRL] else 1
                if simulation_frame_rate <= 0:
                    simulation_frame_rate = 5 if not keys[pg.K_LCTRL] else 1
        if event.type == pg.MOUSEBUTTONDOWN:
            drag = True
        if event.type == pg.MOUSEBUTTONUP:
            drag = False
            
    screen.fill(WHITE)
    
    if drag:
        x,y = pg.mouse.get_pos()
        grid[x//8][y//8] = 1
    
    if not paused:
        grid = new_generation(grid)
        
    surface = pg.surfarray.make_surface(colors[grid])
    surface = pg.transform.scale(surface, (800, 800))  # Scaled a bit.
    screen.blit(surface, (0, 0))
    write_text("Press space to pause", 5, 5, 14, screen)
    write_text(f"Paused: {paused}", 5, 20, 14, screen)
    write_text("W - Speed up | S - Slow down | Hold ctrl for fine control", 5, 35, 14, screen)
    write_text(f"Frame rate: {simulation_frame_rate}", 5, 50, 14, screen)
    write_text(f"FPS: {round(clock.get_fps(), 2)}", 5, 65, 14, screen)
    pg.display.flip()

Langtons Ant

Implementation of Langtons Ant in python.

def new_generation(board: list, ant_position: list, direction: int) -> tuple:
    directions = [(-1, 0), (0, 1), (1,0), (0,-1)]

    x, y = ant_position

    direction = (direction + (1 if board[x][y] else -1)) % 4

    move_x, move_y = directions[direction]
    ant_position[0] += move_x
    ant_position[1] += move_y

    board[x][y] = 0 if board[x][y] else 1

    return board, ant_position, direction

Creating a UI with pygame.

import pygame as pg
import sys
import numpy as np

def new_generation(board: list, ant_position: list, direction: int) -> tuple:
    directions = [(-1, 0), (0, 1), (1,0), (0,-1)]

    x, y = ant_position

    direction = (direction + (1 if board[x][y] else -1)) % 4

    move_x, move_y = directions[direction]
    ant_position[0] += move_x
    ant_position[1] += move_y

    board[x][y] = 0 if board[x][y] else 1

    return board, ant_position, direction

def write_text(string, x, y, font_size, screen):
    text = pg.font.Font('freesansbold.ttf', font_size).render(string, True, (0, 0, 0))
    text_rect = text.get_rect()
    text_rect.topleft = (x, y)
    screen.blit(text, text_rect)

pg.init()

screen_size = (800, 800)
simulation_size = (100, 100)
simulation_frame_rate = 30
frame_cap = 100
iteration = 0
screen = pg.display.set_mode(screen_size)

clock = pg.time.Clock()

WHITE = [255, 255, 255]
BLACK = [0, 0, 0]
RED = [255, 0, 0]

colors = np.array([WHITE, BLACK, RED])
grid = np.zeros(shape=simulation_size, dtype=int)
ant_position = [simulation_size[0]//2, simulation_size[1]//2]
direction = 3
paused = True

while True:
    clock.tick(simulation_frame_rate)

    for event in pg.event.get():
        if event.type == pg.QUIT:
            pg.quit()
            sys.exit()
        if event.type == pg.KEYDOWN:
            keys = pg.key.get_pressed()
            if keys[pg.K_SPACE]:
                paused = not paused
            if keys[pg.K_w]:
                simulation_frame_rate += 5 if not keys[pg.K_LCTRL] else 1
                if simulation_frame_rate >= frame_cap:
                    simulation_frame_rate = frame_cap
            if keys[pg.K_s]:
                simulation_frame_rate -= 5 if not keys[pg.K_LCTRL] else 1
                if simulation_frame_rate <= 0:
                    simulation_frame_rate = 5 if not keys[pg.K_LCTRL] else 1
            
    screen.fill(WHITE)
    
    
    if not paused:
        grid, ant_position, direction = new_generation(grid, ant_position, direction)
        iteration += 1

    
    temp = grid[ant_position[0], ant_position[1]]
    grid[ant_position[0], ant_position[1]] = 2
    surface = pg.surfarray.make_surface(colors[grid])
    grid[ant_position[0], ant_position[1]] = temp
    surface = pg.transform.scale(surface, (800, 800))  # Scaled a bit.
    screen.blit(surface, (0, 0))
    write_text("Press space to pause", 5, 5, 14, screen)
    write_text(f"Paused: {paused}", 5, 20, 14, screen)
    write_text("W - Speed up | S - Slow down | Hold ctrl for fine control", 5, 35, 14, screen)
    write_text(f"Frame rate: {simulation_frame_rate}", 5, 50, 14, screen)
    write_text(f"FPS: {round(clock.get_fps(), 2)}", 5, 65, 14, screen)
    write_text(f"Iteration: {iteration}", 5, 80, 14, screen)
    pg.display.flip()

Wa Tor

Battle between sharks and fish.

import random
def fish_movement(i, j, grid, new_grid, colors):
    options = [[1,0],[0,1],[-1,0],[0,-1]]
    spaces = []
    for x, y in options:
        try:
            if grid[i+x][j+y][0] == 0 and new_grid[i+x][j+y][0] == 0:
                spaces.append([x,y])
        except IndexError:
            ... # Out of bounds
    if spaces:
        space = random.choice(spaces)
        if grid[i][j][1]-1 <= 0:
            new_grid[i+space[0]][j+space[1]] = [1, 5]
            colors[i+space[0]][j+space[1]] = 1
            new_grid[i][j] = [1, 5]
            colors[i][j] = 1
        else:
            new_grid[i+space[0]][j+space[1]] = [1, grid[i][j][1]-1]
            colors[i+space[0]][j+space[1]] = 1
    else:
        new_grid[i][j] = [1, grid[i][j][1]-1]
        colors[i][j] = 1
        
def shark_movement(i, j, grid, new_grid, colors):
        if grid[i][j][2] == 0:
            # Shark dies
            return
        options = [[1,0],[0,1],[-1,0],[0,-1]]
        spaces = []
        alt_spaces = []
        fish = False
        for x, y in options:
            try:
                if grid[i+x][j+y][0] == 1 and new_grid[i+x][j+y][0] == 1:
                    spaces.append([x,y])
                    fish = True
            except IndexError:
                ... # Out of bounds
            try:
                if grid[i+x][j+y][0] == 0 and new_grid[i+x][j+y][0] == 0:
                    alt_spaces.append([x,y])
            except IndexError:
                ... # Out of bounds
        if not spaces:
            spaces = alt_spaces
        if spaces:
            space = random.choice(spaces)
            if fish:
                grid[i][j][2] += 5
            if grid[i][j][1]-1 <= 0:
                new_grid[i+space[0]][j+space[1]] = [2, 20, grid[i][j][2]-1]
                colors[i+space[0]][j+space[1]] = 2
                new_grid[i][j] = [2, 20, 15]
                colors[i][j] = 2
            else:
                new_grid[i+space[0]][j+space[1]] = [2, grid[i][j][1]-1, grid[i][j][2]-1]
                colors[i+space[0]][j+space[1]] = 2
        else:
            new_grid[i][j] = [2, grid[i][j][1]-1, grid[i][j][2]-1]
            colors[i][j] = 2
        
def new_generation(grid: list) -> tuple:
    new_grid = [[[0] for _ in range(len(grid))] for _ in range(len(grid[0]))]
    colors = [[0 for _ in range(len(grid))] for _ in range(len(grid[0]))]
    # for i, line in enumerate(grid):
        # for j, index in enumerate(line):
    coords = [(x,y) for x in range(len(grid)) for y in range(len(grid[0]))]
    random.shuffle(coords)
    for i, j in coords:
        # Nothing
        if grid[i][j][0] == 0:
            continue
        # Fish
        if grid[i][j][0] == 1:
            fish_movement(i, j, grid, new_grid, colors)
        # Shark
        if grid[i][j][0] == 2:
            shark_movement(i, j, grid, new_grid, colors)

Creating a UI with pygame.

import pygame as pg
import sys
import numpy as np
import random

def fish_movement(i, j, grid, new_grid, colors):
    options = [[1,0],[0,1],[-1,0],[0,-1]]
    spaces = []
    for x, y in options:
        try:
            if grid[i+x][j+y][0] == 0 and new_grid[i+x][j+y][0] == 0:
                spaces.append([x,y])
        except IndexError:
            ... # Out of bounds
    if spaces:
        space = random.choice(spaces)
        if grid[i][j][1]-1 <= 0:
            new_grid[i+space[0]][j+space[1]] = [1, 5]
            colors[i+space[0]][j+space[1]] = 1
            new_grid[i][j] = [1, 5]
            colors[i][j] = 1
        else:
            new_grid[i+space[0]][j+space[1]] = [1, grid[i][j][1]-1]
            colors[i+space[0]][j+space[1]] = 1
    else:
        new_grid[i][j] = [1, grid[i][j][1]-1]
        colors[i][j] = 1
        
def shark_movement(i, j, grid, new_grid, colors):
        if grid[i][j][2] == 0:
            # Shark dies
            return
        options = [[1,0],[0,1],[-1,0],[0,-1]]
        spaces = []
        alt_spaces = []
        fish = False
        for x, y in options:
            try:
                if grid[i+x][j+y][0] == 1 and new_grid[i+x][j+y][0] == 1:
                    spaces.append([x,y])
                    fish = True
            except IndexError:
                ... # Out of bounds
            try:
                if grid[i+x][j+y][0] == 0 and new_grid[i+x][j+y][0] == 0:
                    alt_spaces.append([x,y])
            except IndexError:
                ... # Out of bounds
        if not spaces:
            spaces = alt_spaces
        if spaces:
            space = random.choice(spaces)
            if fish:
                grid[i][j][2] += 5
            if grid[i][j][1]-1 <= 0:
                new_grid[i+space[0]][j+space[1]] = [2, 20, grid[i][j][2]-1]
                colors[i+space[0]][j+space[1]] = 2
                new_grid[i][j] = [2, 20, 15]
                colors[i][j] = 2
            else:
                new_grid[i+space[0]][j+space[1]] = [2, grid[i][j][1]-1, grid[i][j][2]-1]
                colors[i+space[0]][j+space[1]] = 2
        else:
            new_grid[i][j] = [2, grid[i][j][1]-1, grid[i][j][2]-1]
            colors[i][j] = 2
        
def new_generation(grid: list) -> tuple:
    new_grid = [[[0] for _ in range(len(grid))] for _ in range(len(grid[0]))]
    colors = [[0 for _ in range(len(grid))] for _ in range(len(grid[0]))]
    # for i, line in enumerate(grid):
        # for j, index in enumerate(line):
    coords = [(x,y) for x in range(len(grid)) for y in range(len(grid[0]))]
    random.shuffle(coords)
    for i, j in coords:
        # Nothing
        if grid[i][j][0] == 0:
            continue
        # Fish
        if grid[i][j][0] == 1:
            fish_movement(i, j, grid, new_grid, colors)
        # Shark
        if grid[i][j][0] == 2:
            shark_movement(i, j, grid, new_grid, colors)

    return new_grid, colors

def write_text(string, x, y, font_size, screen):
    text = pg.font.Font('freesansbold.ttf', font_size).render(string, True, (0, 0, 0))
    text_rect = text.get_rect()
    text_rect.topleft = (x, y)
    screen.blit(text, text_rect)

pg.init()

screen_size = (800, 800)
simulation_size = (100, 100)
simulation_frame_rate = 5
frame_cap = 30
iteration = 0
screen = pg.display.set_mode(screen_size)

clock = pg.time.Clock()

WHITE = [255, 255, 255]
RED = [200, 50, 50]
GREEN = [50, 200, 50]

colors = np.array([WHITE, GREEN, RED])
# grid = np.zeros(shape=simulation_size, dtype=int)
animal_options = [[0], [1,5], [2,20,15]]
# print(np.random.choice([0,1,2], 1,  p=[0.8, 0.2, 0]))
grid = [[animal_options[np.random.choice([0,1,2], 1,  p=[0.6, 0.3, 0.1])[0]] for _ in range(simulation_size[0])] for _ in range(simulation_size[1])]
color = [[grid[i][j][0] for i in range(simulation_size[0])] for j in range(simulation_size[1])]
ant_position = [simulation_size[0]//2, simulation_size[1]//2]
direction = 3
paused = True

while True:
    clock.tick(simulation_frame_rate)

    for event in pg.event.get():
        if event.type == pg.QUIT:
            pg.quit()
            sys.exit()
        if event.type == pg.KEYDOWN:
            keys = pg.key.get_pressed()
            if keys[pg.K_SPACE]:
                paused = not paused
            if keys[pg.K_w]:
                simulation_frame_rate += 5 if not keys[pg.K_LCTRL] else 1
                if simulation_frame_rate >= frame_cap:
                    simulation_frame_rate = frame_cap
            if keys[pg.K_s]:
                simulation_frame_rate -= 5 if not keys[pg.K_LCTRL] else 1
                if simulation_frame_rate <= 0:
                    simulation_frame_rate = 5 if not keys[pg.K_LCTRL] else 1
            
    screen.fill(WHITE)
    
    
    if not paused:
        grid, color = new_generation(grid)
        iteration += 1

    surface = pg.surfarray.make_surface(colors[color])
    surface = pg.transform.scale(surface, (800, 800))  # Scaled a bit.
    screen.blit(surface, (0, 0))
    write_text("Press space to pause", 5, 5, 14, screen)
    write_text(f"Paused: {paused}", 5, 20, 14, screen)
    write_text("W - Speed up | S - Slow down | Hold ctrl for fine control", 5, 35, 14, screen)
    write_text(f"Frame rate: {simulation_frame_rate}", 5, 50, 14, screen)
    write_text(f"FPS: {round(clock.get_fps(), 2)}", 5, 65, 14, screen)
    write_text(f"Iteration: {iteration}", 5, 80, 14, screen)
    pg.display.flip()